package com.ruoyi.learn.java.design.pattern.behavior;

/**
 * 策略模式 (Strategy Pattern) 示例：实现购物车的多种折扣策略。
 */
/**
 * 策略模式 (Strategy Pattern) 示例：实现购物车的多种折扣策略。
 */
public class StrategyPatternDemo {

    //region ========== 1. 策略 (Strategy) ==========

    /**
     * 策略接口：折扣策略
     * 定义了所有折扣算法必须实现的公共方法。
     * calculateDiscount 方法接收总价，返回折扣后的价格。
     */
    public interface DiscountStrategy {
        /**
         * 计算应用折扣后的价格。
         * @param originalPrice 原始总价
         * @return 折扣后的价格
         */
        double calculateDiscount(double originalPrice);
    }

    //endregion

    //region ========== 2. 具体策略 (ConcreteStrategy) ==========

    /**
     * 具体策略：无折扣
     * 不应用任何折扣，返回原价。
     */
    public static class NoDiscountStrategy implements DiscountStrategy {
        @Override
        public double calculateDiscount(double originalPrice) {
            System.out.println("    > 【策略】无折扣: " + originalPrice + " -> " + originalPrice);
            return originalPrice;
        }
    }

    /**
     * 具体策略：固定折扣
     * 按固定的折扣率进行打折（如 0.9 表示 9 折）。
     */
    public static class FixedDiscountStrategy implements DiscountStrategy {
        private final double discountRate; // 折扣率，如 0.9

        public FixedDiscountStrategy(double discountRate) {
            if (discountRate <= 0 || discountRate > 1) {
                throw new IllegalArgumentException("折扣率必须在 (0, 1] 范围内");
            }
            this.discountRate = discountRate;
        }

        @Override
        public double calculateDiscount(double originalPrice) {
            double discountedPrice = originalPrice * discountRate;
            System.out.println("    > 【策略】固定折扣 " + (discountRate * 10) + " 折: " +
                    originalPrice + " -> " + String.format("%.2f", discountedPrice));
            return discountedPrice;
        }
    }

    /**
     * 具体策略：满减折扣
     * 当原始价格达到某个阈值时，减免固定金额。
     */
    public static class ThresholdDiscountStrategy implements DiscountStrategy {
        private final double threshold; // 满足条件的阈值，如 100
        private final double reduction;  // 减免的金额，如 20

        public ThresholdDiscountStrategy(double threshold, double reduction) {
            if (threshold <= 0 || reduction < 0) {
                throw new IllegalArgumentException("阈值必须大于0，减免金额不能为负");
            }
            this.threshold = threshold;
            this.reduction = reduction;
        }

        @Override
        public double calculateDiscount(double originalPrice) {
            if (originalPrice >= threshold) {
                double discountedPrice = originalPrice - reduction;
                System.out.println("    > 【策略】满 " + threshold + " 减 " + reduction + ": " +
                        originalPrice + " -> " + String.format("%.2f", discountedPrice));
                return discountedPrice;
            } else {
                System.out.println("    > 【策略】未满足满减条件 (" + originalPrice + " < " + threshold + ")");
                return originalPrice;
            }
        }
    }

    /**
     * 具体策略：会员折扣
     * 会员享受固定折扣率（如 0.8 表示 8 折）。
     * 这个策略可以独立使用，也可以与其他策略组合（本例仅展示独立使用）。
     */
    public static class MemberDiscountStrategy implements DiscountStrategy {
        private static final double MEMBER_RATE = 0.8; // 会员 8 折

        @Override
        public double calculateDiscount(double originalPrice) {
            double discountedPrice = originalPrice * MEMBER_RATE;
            System.out.println("    > 【策略】会员折扣 8 折: " +
                    originalPrice + " -> " + String.format("%.2f", discountedPrice));
            return discountedPrice;
        }
    }

    //endregion

    //region ========== 3. 上下文 (Context) ==========

    /**
     * 上下文：购物车
     * 使用 DiscountStrategy 接口来计算折扣。
     * 购物车本身不关心具体使用哪种折扣算法，只负责调用策略。
     */
    public static class ShoppingCart {
        // 购物车中的商品列表
        private java.util.List<Product> items;
        // 持有对当前折扣策略的引用
        private DiscountStrategy discountStrategy;

        public ShoppingCart() {
            this.items = new java.util.ArrayList<>();
            // 默认策略：无折扣
            this.discountStrategy = new NoDiscountStrategy();
        }

        /**
         * 添加商品到购物车
         */
        public void addItem(Product product) {
            items.add(product);
        }

        /**
         * 计算商品总额（未打折）
         */
        public double calculateTotal() {
            return items.stream().mapToDouble(Product::getPrice).sum();
        }

        /**
         * 设置当前使用的折扣策略
         * 这是策略模式的关键：允许在运行时动态切换策略。
         * @param strategy 新的折扣策略
         */
        public void setDiscountStrategy(DiscountStrategy strategy) {
            if (strategy != null) {
                this.discountStrategy = strategy;
                System.out.println("  > 【购物车】折扣策略已切换为: " + strategy.getClass().getSimpleName());
            } else {
                System.out.println("  > 【购物车】警告：折扣策略不能为 null，保持原策略。");
            }
        }

        /**
         * 计算最终价格（应用折扣后）
         * 将计算折扣的职责委托给当前的 discountStrategy。
         */
        public double calculateFinalPrice() {
            double total = calculateTotal();
            System.out.println("  > 【购物车】商品总额: " + String.format("%.2f", total));
            // 委托给策略对象计算折扣
            return discountStrategy.calculateDiscount(total);
        }

        /**
         * 结账
         */
        public void checkout() {
            System.out.println("\n=== 结账流程开始 ===");
            double finalPrice = calculateFinalPrice();
            System.out.println("  > 【购物车】您需支付: " + String.format("%.2f", finalPrice));
            System.out.println("=== 结账流程结束 ===\n");
        }
    }

    /**
     * 商品类（辅助类）
     */
    public static class Product {
        private String name;
        private double price;

        public Product(String name, double price) {
            this.name = name;
            this.price = price;
        }

        public String getName() { return name; }
        public double getPrice() { return price; }

        @Override
        public String toString() {
            return name + " (¥" + String.format("%.2f", price) + ")";
        }
    }

    //endregion

    //region ========== 4. 主函数 (Main) - 演示策略模式 ==========
     public static void main(String[] args) {
            System.out.println("=== 策略模式演示 ===\n");

            // 1. 创建购物车和商品
            System.out.println("1. 创建购物车和商品...");
            ShoppingCart cart = new ShoppingCart();

            Product p1 = new Product("笔记本电脑", 5999.00);
            Product p2 = new Product("无线鼠标", 199.00);
            Product p3 = new Product("机械键盘", 499.00);

            cart.addItem(p1);
            cart.addItem(p2);
            cart.addItem(p3);

            // 2. 演示不同策略下的结账
            System.out.println("2. 演示不同折扣策略...\n");

            // --- 场景 1: 默认无折扣 ---
            System.out.println("场景 1: 使用默认【无折扣】策略");
            cart.checkout(); // 使用默认的 NoDiscountStrategy

            // --- 场景 2: 应用固定折扣 (9折) ---
            System.out.println("场景 2: 切换为【固定折扣】策略 (9折)");
            cart.setDiscountStrategy(new FixedDiscountStrategy(0.9));
            cart.checkout();

            // --- 场景 3: 应用满减折扣 (满 5000 减 500) ---
            System.out.println("场景 3: 切换为【满减折扣】策略 (满 5000 减 500)");
            cart.setDiscountStrategy(new ThresholdDiscountStrategy(5000, 500));
            cart.checkout();

            // --- 场景 4: 应用会员折扣 (8折) ---
            System.out.println("场景 4: 切换为【会员折扣】策略 (8折)");
            cart.setDiscountStrategy(new MemberDiscountStrategy());
            cart.checkout();

            // 3. 动态切换的优势
            System.out.println("--- 策略模式的优势 ---");
            System.out.println("• **开闭原则**: 增加新折扣策略（如'买一送一'）只需添加新类，");
            System.out.println("  无需修改 ShoppingCart 或现有策略代码。");
            System.out.println("• **消除条件语句**: 如果不用策略模式，ShoppingCart 中可能会有");
            System.out.println("  大量的 if-else 来判断使用哪种折扣，代码难以维护。");
            System.out.println("• **运行时切换**: 可以在程序运行时根据用户选择、会员状态、");
            System.out.println("  促销活动等动态地改变行为。");
            System.out.println("• **算法复用**: 同一个折扣策略可以被多个购物车实例复用。");

            // 4. 注意事项
            System.out.println("\n--- 注意事项 ---");
            System.out.println("• 策略接口的设计要合理，方法参数要能提供算法所需的所有信息。");
            System.out.println("• 上下文（如 ShoppingCart）不应直接访问具体策略的内部细节。");
            System.out.println("• 如果策略对象需要维护状态，需注意其生命周期和线程安全性。");
            System.out.println("• 策略模式会增加类的数量（每个策略一个类），但通常这是可接受的代价。");

            // 5. 与状态模式的区别
            System.out.println("\n--- 与状态模式 (State Pattern) 的区别 ---");
            System.out.println("• **策略模式**: 策略之间是**独立**的，客户端明确选择使用哪个策略。");
            System.out.println("  上下文的行为由外部设置的策略决定。");
            System.out.println("• **状态模式**: 状态之间有**转换关系**，状态的改变通常由上下文或");
            System.out.println("  状态自身的行为触发，转换是自动的。上下文的行为由其当前状态决定。");
            System.out.println("  (例如：订单状态：待支付 -> 已支付 -> 已发货 -> 已完成)");

            // 6. 与模板方法模式的区别
            System.out.println("\n--- 与模板方法模式 (Template Method Pattern) 的区别 ---");
            System.out.println("• **策略模式**: 使用**组合**（Has-A），算法被封装在独立的对象中，");
            System.out.println("  通过委托来调用。更灵活，策略可以在运行时更换。");
            System.out.println("• **模板方法模式**: 使用**继承**（Is-A），算法的骨架在抽象父类中定义，");
            System.out.println("  具体步骤由子类实现。算法骨架是固定的，子类不能改变骨架。");
        }

        //endregion
    }
